/**
 * @file     AnalyzeStructures.h
 * @brief    Responsible for the processing of secondary structures.
 *
 * @author   Franco Riberi
 * @email    fgriberi AT gmail.com
 *
 * Contents: Header file for titere providing AnalyzeStructure interface.
 *
 * System:   titere: Target Ideal TEmperature Rna Expression
 * Language: C++
 *
 * @date January 8, 2014
 *
 * Copyright (C) 2014 Franco Riberi, FuDePAN
 *
 * This file is part of the titere.
 *
 * Titere is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Titere is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with titere. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef ANALYZE_STRUCTURES_H
#define ANALYZE_STRUCTURES_H

#include <list>
#include <vector>
#include <fideo/fideo.h>
#include <biopp/biopp.h>
#include "titere/Types.h"
#include "titere/PredicateRule.h"
#include "titere/TableOutput.h"

namespace NSTitere
{

/**
 * @brief Responsible to analyze a secondary structure in a range of different temperatures
 */
class AnalyzeStructure
{
public:

    /** @brief Constructor of class parametrized
     *
     * @param args The necessary arguments
     */
    AnalyzeStructure(const TitereInputArgs& args);

    /**
     * @brief Destructor of class
     */
    ~AnalyzeStructure();

    /**
     * @brief Folds all sequences at different temperatures
     */
    void foldSequences();

    /**
     * @brief Analyzes window of length L on each sequences
     */
    void analyzeWindows();

    /** @brief Represents the rule names */
    static const char START_WITH_ADENINE_OR_URACIL_RULE[];
    static const char FIRST_EIGHT_NUC_RULE[];
    static const char NUC_NINETEEN_RULE[];

private:

    /** @Brief Represents a container for split operations */
    typedef std::vector<std::string> SplitContainer;

    /** @brief Represents all rules to validate the window */
    typedef std::list<IPredicateRule*> Rules;

    /** @brief Represents a container for the secondary structures */
    typedef std::vector<biopp::SecStructure> StructureContainer;

    /** @brief Represents a factory registry for the rules */
    typedef mili::FactoryRegistry<IPredicateRule, std::string> Rule;

    /** @brief Represents a container for all sequences and their folding in different temperatures */
    struct SequencesFoldedContainer
    {
        std::string _description;
        biopp::NucSequence _sequence;
        StructureContainer _structures;
    };

    /** @brief Represents a container for all sequences folded */
    typedef std::list<SequencesFoldedContainer> FoldingContainer;

    /** @brief Represents specific chars for the derived structures data */
    enum OutputChar
    {
        SamePairing      = '0', // paired state in the reference and current structure with the same nucleotide
        UnpairedToPaired = '+', // unpaired in the reference structure and paired in the current structure
        PairedToUnpaired = '-', // paired in the reference structure and unpaired in the current structure
        PairedCrossed    = 'X'  // paired state in the reference and current structure with a different nucleotide
    };

    /** @brief Specific important position of the structure. */
    static const Position INITIAL_POSITION = 0u;
    static const Position SECOND_POSITION = 1u;
    static const Position EIGHT_POSITION = 7u;

    /*
     * @brief Sets all rules that the windows must meet
     */
    void setRules();

    /**
     * @brief Checks that all predicates rules are true
     *
     * @param sequence The specific sequence to check
     * @return True if all rules passed, otherwise false.
     */
    bool checkRules(const biopp::NucSequence& sequence);

    /**
     * @brief Procesess a window
     *
     * @param initialPos       Initial position on the sequence
     * @param seqFoldContainer A spefific sequence with your secondary structures
     */
    void processWindow(Position pos0, const SequencesFoldedContainer& seqFoldContainer);

    /**
     * @brief Processes a sequence over a windows
     *
     * @param seqFoldContainer A spefific sequence to proccess
     */
    void processSequence(const SequencesFoldedContainer& seqFoldContainer);

    /**
     * @brief Gets a subsequence in a specific range
     *
     * @param from        Initial range value
     * @param to          Final range value
     * @param originalSeq Input sequence
     * @param subSequence To fill with the subsequence in the range [from-to]
     */
    void getSubSequence(Position from, Position to, const biopp::NucSequence& originalSeq, biopp::NucSequence& subSequence) const;

    /**
     * @brief Folds sequence in all temperatures
     *
     * @param sequences       Input sequence
     * @param structure       To fill with the secondary structure at a certain temperature
     * @param containerFolded Container to fill with the fold results
     */
    void foldInAllTemps(const biopp::NucSequence& sequence, SequencesFoldedContainer& containerFolded);

    /**
     * @brief Process a nucleotide when this is paired on the secondary structure of reference
     *
     * @param index              Specific position in the sequence
     * @param currentStructure   Current secondary structure
     * @param referenceStructure Secondary structure of reference
     * @param diffStruct         To append the specific value
     */
    void processPairedOnReferenceStruct(Position index, const biopp::SecStructure& currentStructure,
                                        const biopp::SecStructure& referenceStruct, std::string& diffStruct);

    /**
     * @brief Process a nucleotide when this is unpaired on the secondary structure of reference
     *
     * @param index       Specific position in the sequence
     * @param structure   Current secondary structure
     * @param diffStruct  To append the specific value
     */
    void processUnpairedOnReferenceStruct(Position index, const biopp::SecStructure& structure, std::string& diffStruct);

    /**
     * @brief Calculates the derived structure between two secondary structures in specific range
     *
     * @param from             Initial range value
     * @param to               Final range value
     * @param referenceStruct  Secondary structure of reference
     * @param currentStructure Current secondary structure
     * @param diffStruct       To fill with the difference between the structures
     */
    void calculateDerivedStructureData(Position from, Position to, const biopp::SecStructure& referenceStruct,
                                       const biopp::SecStructure& currentStructure, std::string& diffStruct);

    /**
     * @brief Calculates the unpaired nucleotids
     */
    void calculateUnpaired(const biopp::SecStructure structure, NucleotidsAmount& unpairedCounter, NucleotidsAmount& headUnpairedCounter);

    /**
     * @brief Counts the unpaired nucleotides in a specific substructure
     *
     * @param from      Initial position
     * @param to        Final position
     * @param structure The secondary structure
     * @return The amount of unpaired nucleotides in [from-to]
     */
    NucleotidsAmount countNucleotidesUnpaired(Position from, Position to, const biopp::SecStructure& structure);

    /** @brief Arguments from user */
    const TitereInputArgs _args;

    /** @brief To load the folding result (sequence and structure) */
    FoldingContainer _sequencesFolded;

    /** @brief Use the fold service from fideo library */
    fideo::IFold* _folder;

    /** @brief To load the all rules for check the windows */
    Rules _rules;

    /** @brief Represents the output file */
    TableOutput _outputFile;

    /** @brief Output file name */
    static const char OUTPUT_FILE_NAME[];

    /** @brief Represents the sequence name position */
    static const size_t SEQ_NAME = 3u;

    /** @brief Represents the minimum amount of secondary structure necessary */
    static const size_t MINIMAL_AMOUNT_OF_STRUCTURES = 2u;

#define ANALYZE_STRUCTURES_INLINE_H
#include "AnalyzeStructuresInline.h"
#undef ANALYZE_STRUCTURES_INLINE_H
};

} // namespace NSTitere

#endif /* ANALYZE_STRUCTURES_H */